package ireader.presentation.ui.reader.reverse_swip_refresh

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
import kotlin.math.min

internal class ReverseCircularProgressPainter(private val clockwise: Boolean = true) : Painter() {
    var color by mutableStateOf(Color.Unspecified)
    var alpha by mutableStateOf(1f)
    var arcRadius by mutableStateOf(0.dp)
    var strokeWidth by mutableStateOf(5.dp)
    var arrowEnabled by mutableStateOf(false)
    var arrowWidth by mutableStateOf(0.dp)
    var arrowHeight by mutableStateOf(0.dp)
    var arrowScale by mutableStateOf(1f)

    private val arrow: Path by lazy {
        Path().apply { fillType = PathFillType.EvenOdd }
    }

    var startTrim by mutableStateOf(0f)
    var endTrim by mutableStateOf(0f)
    var rotation by mutableStateOf(0f)

    override val intrinsicSize: Size
        get() = Size.Unspecified

    override fun applyAlpha(alpha: Float): Boolean {
        this.alpha = alpha
        return true
    }

    override fun DrawScope.onDraw() {
        rotate(degrees = rotation) {
            val arcRadius = arcRadius.toPx() + strokeWidth.toPx() / 2f
            val arcBounds = Rect(
                size.center.x - arcRadius,
                size.center.y - arcRadius,
                size.center.x + arcRadius,
                size.center.y + arcRadius
            )
            val startAngle: Float
            val endAngle: Float
            val sweepAngle: Float

            if (clockwise) {
                startAngle = (startTrim + rotation) * 360
                endAngle = (endTrim + rotation) * 360
                sweepAngle = endAngle - startAngle
            } else {
                startAngle = (startTrim - rotation) * 360
                endAngle = (endTrim - rotation) * 360
                sweepAngle = startAngle - endAngle
            }

            drawArc(
                color = color,
                alpha = alpha,
                startAngle = startAngle,
                sweepAngle = sweepAngle,
                useCenter = false,
                topLeft = arcBounds.topLeft,
                size = arcBounds.size,
                style = Stroke(
                    width = strokeWidth.toPx(),
                    cap = StrokeCap.Square
                )
            )
            if (arrowEnabled) {
                drawArrow(startAngle, sweepAngle, arcBounds)
            }
        }
    }

    private fun DrawScope.drawArrow(startAngle: Float, sweepAngle: Float, bounds: Rect) {
        arrow.reset()
        arrow.moveTo(0f, 0f)
        arrow.lineTo(
            x = arrowWidth.toPx() * arrowScale,
            y = 0f
        )
        val arrowHeightPx = if (clockwise) {
            arrowHeight.toPx()
        } else {
            -arrowHeight.toPx()
        }

        arrow.lineTo(
            x = arrowWidth.toPx() * arrowScale / 2,
            y = arrowHeightPx * arrowScale
        )
        val radius = min(bounds.width, bounds.height) / 2f
        val inset = arrowWidth.toPx() * arrowScale / 2f
        arrow.translate(
            Offset(
                x = radius + bounds.center.x - inset,
                y = bounds.center.y + strokeWidth.toPx() / 2f
            )
        )
        arrow.close()
        rotate(degrees = startAngle + sweepAngle) {
            drawPath(
                path = arrow,
                color = color,
                alpha = alpha
            )
        }
    }
}
